/**
*****************************************************************************************
*     Copyright(c) 2015, Realtek Semiconductor Corporation. All rights reserved.
*****************************************************************************************
* @file     switch_model.c
* @brief    Source file for switch model.
* @details  Data types and external functions declaration.
* @author   hector_huang
* @date     2020-06-16
* @version  v1.0
* *************************************************************************************
*/

#include "switch_model.h"

static mesh_msg_send_cause_t switch_send(mesh_model_info_p pmodel_info, uint16_t dst,
                                         uint8_t *pmsg, uint16_t msg_len, uint16_t app_key_index,
                                         uint32_t delay_time)
{
    mesh_msg_t mesh_msg;
    mesh_msg.pmodel_info = pmodel_info;
    access_cfg(&mesh_msg);
    mesh_msg.pbuffer = pmsg;
    mesh_msg.msg_len = msg_len;
    if (0 != dst)
    {
        mesh_msg.dst = dst;
        mesh_msg.app_key_index = app_key_index;
    }
    mesh_msg.delay_time = delay_time;
    return access_send(&mesh_msg);
}

static mesh_msg_send_cause_t switch_addr_status(mesh_model_info_p pmodel_info, uint16_t dst,
                                                uint8_t addr[6], uint16_t app_key_index,
                                                uint32_t delay_time)
{
    switch_addr_status_t msg;
    ACCESS_OPCODE_BYTE(msg.opcode, MESH_MSG_SWITCH_ADDR_STATUS);
    memcpy(msg.addr, addr, 6);
    return switch_send(pmodel_info, dst, (uint8_t *)&msg, sizeof(msg), app_key_index, delay_time);
}

mesh_msg_send_cause_t switch_heartbeat(const mesh_model_info_p pmodel_info, uint16_t dst,
                                       uint16_t app_key_index, uint8_t mac_addr[6])
{
    switch_heartbeat_t msg;
    ACCESS_OPCODE_BYTE(msg.opcode, MESH_MSG_SWITCH_HEARTBEAT);
    memcpy(msg.mac_addr, mac_addr, 6);
    return switch_send(pmodel_info, dst, (uint8_t *)&msg, sizeof(msg), app_key_index, 0);
}

mesh_msg_send_cause_t switch_addr_get(const mesh_model_info_p pmodel_info, uint16_t dst,
                                      uint16_t app_key_index)
{
    switch_addr_get_t msg;
    ACCESS_OPCODE_BYTE(msg.opcode, MESH_MSG_SWITCH_ADDR_GET);
    return switch_send(pmodel_info, dst, (uint8_t *)&msg, sizeof(msg), app_key_index, 0);
}

mesh_msg_send_cause_t switch_datatrans(const mesh_model_info_p pmodel_info, uint16_t dst,
                                       uint16_t app_key_index, const uint8_t *pdata, uint16_t len)
{
    switch_datatrans_t msg;
    ACCESS_OPCODE_BYTE(msg.opcode, MESH_MSG_SWITCH_DATATRANS);
    memcpy(msg.data, pdata, len);
    return switch_send(pmodel_info, dst, (uint8_t *)&msg,
                       len + ACCESS_OPCODE_SIZE(MESH_MSG_SWITCH_DATATRANS), app_key_index, 0);
}

mesh_msg_send_cause_t switch_reset(const mesh_model_info_p pmodel_info, uint16_t dst,
                                   uint16_t app_key_index, uint8_t mac_addr[6])
{
    switch_heartbeat_t msg;
    ACCESS_OPCODE_BYTE(msg.opcode, MESH_MSG_SWITCH_RESET);
    memcpy(msg.mac_addr, mac_addr, 6);
    return switch_send(pmodel_info, dst, (uint8_t *)&msg, sizeof(msg), app_key_index, 0);
}

static bool switch_receive(mesh_msg_p pmesh_msg)
{
    bool ret = true;
    mesh_model_info_t *pmodel_info = pmesh_msg->pmodel_info;
    switch (pmesh_msg->access_opcode)
    {
    case MESH_MSG_SWITCH_HEARTBEAT:
        if (pmesh_msg->msg_len == sizeof(switch_heartbeat_t))
        {
            switch_heartbeat_t *pmsg = (switch_heartbeat_t *)pmesh_msg->pbuffer;
            switch_model_heartbeat_t heartbeat = {pmesh_msg->src};
            memcpy(heartbeat.mac_addr, pmsg->mac_addr, 6);
            if (NULL != pmodel_info->model_data_cb)
            {
                pmodel_info->model_data_cb(pmodel_info, SWITCH_MODEL_HEARTBEAT, &heartbeat);
            }
        }
        break;
    case MESH_MSG_SWITCH_ADDR_GET:
        if (pmesh_msg->msg_len == sizeof(switch_addr_get_t))
        {
            switch_model_addr_get_t addr_get = {pmesh_msg->src, {0, 0, 0, 0, 0, 0}};
            if (NULL != pmodel_info->model_data_cb)
            {
                pmodel_info->model_data_cb(pmodel_info, SWITCH_MODEL_ADDR_GET, &addr_get);
            }

            switch_addr_status(pmodel_info, pmesh_msg->src, addr_get.addr, pmesh_msg->app_key_index, 0);
        }
        break;
    case MESH_MSG_SWITCH_ADDR_STATUS:
        if (pmesh_msg->msg_len == sizeof(switch_addr_status_t))
        {
            switch_addr_status_t *pmsg = (switch_addr_status_t *)pmesh_msg->pbuffer;
            switch_model_addr_status_t addr_status = {pmesh_msg->src};
            memcpy(addr_status.addr, pmsg->addr, 6);
            if (NULL != pmodel_info->model_data_cb)
            {
                pmodel_info->model_data_cb(pmodel_info, SWITCH_MODEL_ADDR_STATUS, &addr_status);
            }
        }
        break;
    case MESH_MSG_SWITCH_DATATRANS:
        {
            switch_datatrans_t *pmsg = (switch_datatrans_t *)pmesh_msg->pbuffer;
            switch_model_datatrans_t data_trans = {pmesh_msg->src};
            data_trans.pdata = pmsg->data;
            data_trans.data_len = pmesh_msg->msg_len - ACCESS_OPCODE_SIZE(MESH_MSG_SWITCH_DATATRANS);
            if (NULL != pmodel_info->model_data_cb)
            {
                pmodel_info->model_data_cb(pmodel_info, SWITCH_MODEL_DATATRANS, &data_trans);
            }
        }
        break;
    case MESH_MSG_SWITCH_RESET:
        if (pmesh_msg->msg_len == sizeof(switch_reset_t))
        {
            switch_reset_t *pmsg = (switch_reset_t *)pmesh_msg->pbuffer;
            uint8_t bt_addr[6];
            gap_get_param(GAP_PARAM_BD_ADDR, bt_addr);
            if (0 == memcmp(pmsg->mac_addr, bt_addr, 6))
            {
                printi("switch_receive: node reseted by remote device 0x%04x", pmesh_msg->src);
                mesh_node_reset();
            }
        }
    default:
        ret = false;
        break;
    }

    return ret;
}

bool switch_model_reg(uint8_t element_index, mesh_model_info_p pmodel_info)
{
    if (NULL == pmodel_info)
    {
        return false;
    }

    pmodel_info->model_id = MESH_MODEL_SWITCH;
    if (NULL == pmodel_info->model_receive)
    {
        pmodel_info->model_receive = switch_receive;
        if (NULL == pmodel_info->model_data_cb)
        {
            printw("switch_model_reg: missing model data process callback!");
        }
    }

    return mesh_model_reg(element_index, pmodel_info);
}
